package cn.edu.njnu.opengmpback.Modeling.service;

import cn.edu.njnu.opengmpback.Modeling.CallBack.MyExecStartResultCallback;
import cn.edu.njnu.opengmpback.Modeling.socket.ComputeModelSocket;
import cn.edu.njnu.opengmpback.common.entity.FileInfo;
import cn.edu.njnu.opengmpback.common.entity.FileType;
import cn.edu.njnu.opengmpback.common.utils.FTPFileTransferService;
import cn.edu.njnu.opengmpback.common.utils.ResultUtils;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.RemoveContainerCmd;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Service
public class DockerService {
    private static final Logger log = LoggerFactory.getLogger(DockerService.class);
//    @Autowired
//    DockerDao dockerDao;
    @Autowired  //临时注入
    DockerClient dockerClient;
    int startport=50000;
    @Autowired
    ComputeModelSocket webSocketService;

    String exeId;

    @Value("${containerDir}")
    private String containerDir; //容器工作空间

    @Value("${workSpaceDir}")
    private String workSpaceDir; //用户可操作的位置

    @Autowired
    FTPFileTransferService fileTransferService;

    @Value("${dataStoreDir}")
    private String dataStoreDir;

    @Value("${tempUploadDir}")
    private String tempUploadDir;

    @Value("${dockerServerIp}")
    private String dockerServerIp;

    @Value("${dockerServerPort}")
    private String dockerServerPort;

    @Value("${dockerCaPath}")
    private String dockerCaPath;

    public DockerService() {}

    @PostConstruct // Spring会在注入完成后调用此方法
    public void init() throws IOException {
        dockerClient = createDockerClient();
    }

    private DockerClient createDockerClient() throws IOException {
        // 从配置文件中加载 Docker 相关配置
//        String dockerServerIp = "223.2.40.225"; // Docker 服务器 IP
//        int dockerServerPort = 2376;            // Docker TLS 服务端口
//        String dockerCaPath = "src/main/resources/docker-ca";

        // 构建 Docker 客户端配置
        DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
                .withDockerHost("tcp://" + dockerServerIp + ":" + dockerServerPort)  // Docker 服务端地址
                .withDockerTlsVerify(true)   // 启用 TLS 验证
                .withDockerCertPath(dockerCaPath)  // 证书目录
                .withApiVersion("1.45")
                .build();

        log.info(dockerCaPath);
        // 构建 Docker 客户端
        return DockerClientBuilder.getInstance(config).build();
    }

    public void initEnv(String volumnPath, String projectId) {
        try {
            //创建容器前的准备:创建文件夹和main.py
            String localFilePath = tempUploadDir + projectId;
            File folder = new File(localFilePath);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }
            String filePath = localFilePath + "/main.py";
            Path path = Paths.get(filePath);
            Files.newOutputStream(path).close();

            String filePath1 = localFilePath + "/workflow.py";
            Path path1 = Paths.get(filePath1);
            Files.newOutputStream(path1).close();

            volumnPath.replace("\\","/");
            String dataPath = volumnPath + "/data";
            //为工作空间创建文件夹,包括容器的工作目录和数据存储文件夹,数据存储文件夹是向用户开放的.
            fileTransferService.createRemoteDirAndFile(localFilePath, volumnPath, dataPath);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 创建容器
    public String createContainer(String imageName,String containerName,String volumnPath, String workDir){
        Volume workVolume = new Volume("/" + workDir);
        volumnPath.replace("\\","/");
        Bind workBind=new Bind(volumnPath,workVolume);
        List<Bind> bindList=new ArrayList<Bind>();
        bindList.add(workBind);
        List<Volume> volumeList=new ArrayList<Volume>();
        volumeList.add(workVolume);
        // 创建容器
        CreateContainerResponse container = dockerClient.createContainerCmd(imageName)
                .withName(containerName)
                .withStdinOpen(true)
                .withTty(true)
                .withWorkingDir("/"+workDir)
                .withExposedPorts(ExposedPort.tcp(startport))
                .withVolumes(volumeList)
                .withBinds(bindList)
                .exec();
        String containerId = container.getId();
        startport++;
        return containerId;
    }
    public boolean checkstate(String containerId) throws Exception {
        boolean ifrunning=false;
        List<Container> containers = dockerClient.listContainersCmd().exec();
        for (Container container : containers) {
            if (container.getId().equals(containerId)) {
                ifrunning=true;
            }
        }
        return ifrunning;
    }
    // 启动容器
    public void startContainer(String containerId) throws DockerException, InterruptedException {
        dockerClient.startContainerCmd(containerId).exec();
    }

    // 停止容器
    public void stopContainer(String containerId) throws DockerException, InterruptedException {
        dockerClient.stopContainerCmd(containerId).exec();
    }

    // 删除容器
    public void removeContainer(String containerId) throws DockerException, InterruptedException {
        RemoveContainerCmd removeContainerCmd = dockerClient.removeContainerCmd(containerId);
        removeContainerCmd.withForce(true).exec();
    }


    public List<FileInfo> getCurDirFiles(String projectId, String containerId, String storagePath, List<FileInfo> fileTree){
        try {
            String[] cmd = {"/bin/bash", "-c", "ls -la " + workSpaceDir + storagePath}; //仅向用户开放data文件夹
            ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
                    .withAttachStdout(true)
                    .withAttachStderr(true)
                    .withTty(true)
                    .withAttachStdin(true)
                    .withCmd(cmd)
                    .exec();

            String execId = execCreateCmdResponse.getId();
            OutputStream output = new ByteArrayOutputStream();
            dockerClient.execStartCmd(execId)
                    .exec(new ExecStartResultCallback(output, System.err))
                    .awaitCompletion();
            // 解析命令输出
            String[] files = output.toString().split("\n");
            for (String file : files) {
                // 跳过总计行和 . ..
                String[] fileDetails = file.split("\\s+");
                if (fileDetails.length >= 9) {
                    String fileName = fileDetails[8];
                    if (fileName.equals(".") || fileName.equals("..")) {
                        continue;
                    }
                    String filePath = storagePath + fileName ;

                    // FileInfo 类型
                    Boolean isDirectory = file.startsWith("d");
                    FileType fileType = FileType.unknown;
                    if(isDirectory){
                        fileType = FileType.folder;
                        filePath = filePath + "/";
                    }else{
                        int dotIndex = fileName.lastIndexOf('.');
                        if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
                            String type = fileName.substring(dotIndex + 1).toLowerCase();
                            fileType = setDataType(type);
                        }
                    }

                    FileInfo fileInfo = FileInfo.builder()
                            .fileName(fileName)
                            .filePath(filePath)
                            .fileType(fileType)
                            .absolutePath(containerDir + projectId + "/" + workSpaceDir + filePath)
                            .updateTime(parseFileLastModified(fileDetails))
                            .fileSize(Long.parseLong(fileDetails[4]))
                            .build();

                    // 如果是目录，递归调用
                    if (isDirectory) {
                        List<FileInfo> subFiles = new ArrayList<>();
                        subFiles = getCurDirFiles(projectId, containerId, filePath, subFiles);
                        fileInfo.setChildrenFileList(subFiles);
                    }

                    fileTree.add(fileInfo);
                }
            }
            return fileTree;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static FileType setDataType(String suffix) {
        switch (suffix) {
            case "xlsx":
                return FileType.excel;
            case "xls":
                return FileType.excel;
            case "pptx":
                return FileType.ppt;
            case "ppt":
                return FileType.ppt;
            case "docx":
                return FileType.word;
            case "doc":
                return FileType.word;
            case "csv":
                return FileType.csv;
            case "pdf":
                return FileType.pdf;
            case "zip":
                return FileType.zip;
            case "rar":
                return FileType.zip;
            case "txt":
                return FileType.txt;
            case "gif":
                return FileType.gif;
            case "shp":
                return FileType.shp;
            case "tif":
                return FileType.tif;
            case "mp3":
                return FileType.audio;
            case "wav":
                return FileType.audio;
            case "wma":
                return FileType.audio;
            case "mp4":
                return FileType.video;
            case "avi":
                return FileType.video;
            case "wmv":
                return FileType.video;
            case "mpg":
                return FileType.video;
            case "jpg":
                return FileType.image;
            case "png":
                return FileType.image;
            case "jpeg":
                return FileType.image;
            default:
                return FileType.unknown;
        }
    }

    public Date parseFileLastModified(String [] fileDetails){
        try {
            if (fileDetails.length >= 9) {
                // 获取最后修改时间字符串
                String lastModifiedDateStr = fileDetails[5] + " " + fileDetails[6] + " " + fileDetails[7];

                // 定义 SimpleDateFormat 来解析日期字符串
                SimpleDateFormat dateFormat;

                if (fileDetails[7].contains(":")) {
                    // 时间格式是 `月 日 时间` (e.g., "Aug 15 12:34")
                    dateFormat = new SimpleDateFormat("MMM dd HH:mm", Locale.ENGLISH);
                } else {
                    // 时间格式是 `月 日 年` (e.g., "Aug 15 2023")
                    dateFormat = new SimpleDateFormat("MMM dd yyyy", Locale.ENGLISH);
                }

                // 将解析的时间字符串转化为 Date 对象
                Date lastModifiedDate = dateFormat.parse(lastModifiedDateStr);

                // 如果解析的是当年的时间，加上当前年份
                if (fileDetails[7].contains(":")) {
                    // 获取当前年份
                    SimpleDateFormat currentYearFormat = new SimpleDateFormat("yyyy");
                    String currentYear = currentYearFormat.format(new Date());
                    lastModifiedDateStr = fileDetails[5] + " " + fileDetails[6] + " " + currentYear + " " + fileDetails[7];
                    dateFormat = new SimpleDateFormat("MMM dd yyyy HH:mm", Locale.ENGLISH);
                    lastModifiedDate = dateFormat.parse(lastModifiedDateStr);
                }

                return lastModifiedDate;
            }
        } catch (ParseException e) {
//            e.printStackTrace();
            return null;
        }
        return null;
    }

    public void runCMDInContainer(String projectId, String containerId, String command) {
        try{
            String[] cmd = {"/bin/bash", "-c", command};
            ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
                    .withAttachStdout(true)
                    .withAttachStderr(true)
                    .withTty(true)
                    .withAttachStdin(true)
                    .withCmd(cmd)
                    .exec();

            MyExecStartResultCallback callback=new MyExecStartResultCallback(projectId,webSocketService);
            exeId=execCreateCmdResponse.getId();
            dockerClient.execStartCmd(exeId).exec(callback).awaitCompletion(300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public void runCMD(String projectId, String containerId, String cmd) {
        try{
            ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
                    .withAttachStdout(true)
                    .withAttachStderr(true)
                    .withTty(true)
                    .withAttachStdin(true)
                    .withCmd(cmd)
                    .exec();

            MyExecStartResultCallback callback=new MyExecStartResultCallback(projectId,webSocketService);
            exeId=execCreateCmdResponse.getId();
            dockerClient.execStartCmd(exeId).exec(callback).awaitCompletion(300, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}
